package com.birdboot.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * 请求对象
 * 该类的每一个实例用于表示HTTP协议的一个请求
 * 每个请求都是由三个部分构成的
 * 请求行,消息头,消息正文
 */
public class HttpServletRequest {
    private Socket socket;

    //请求行相关属性
    private String method;//请求方式
    private String uri;//抽象路径
    private String protocol;//协议版本

    private String requestURI;//保存uri中的请求部分(?左侧内容)
    private String queryString;//保存uri中的参数部分(?右侧内容)
    private Map<String,String> parameters = new HashMap<>();//保存每一组参数

    //消息头相关属性
    //key:消息头名字  value:消息头对应的值
    private Map<String,String> headers = new HashMap<>();


    public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
        this.socket = socket;
        //读取请求行
        parseRequestLine();
        //读取消息头
        parseHeaders();
        //读取消息正文
        parseContent();

    }
    //解析请求行
    private void parseRequestLine() throws IOException, EmptyRequestException {
        String line = readLine();
        System.out.println(line);

        if(line.isEmpty()){//如果请求行是空字符串,则说明本次为空请求
            throw new EmptyRequestException();
        }

        String[] data = line.split("\\s");
        method = data[0];
        uri = data[1];
        protocol = data[2];

        //进一步解析uri
        parseURI();

        System.out.println("method:"+method);
        System.out.println("uri:"+uri);
        System.out.println("protocol:"+protocol);
    }

    //进一步解析uri
    private void parseURI(){
        /*
            String requestURI
            String queryString
            Map parameters

            uri有两种情况:
            1:不含有参数的
              例如: /index.html
              直接将uri的值赋值给requestURI即可.

            2:含有参数的
              例如:/regUser?username=fancq&password=123456&nickname=chuanqi&age=22
              将uri中"?"左侧的请求部分赋值给requestURI
              将uri中"?"右侧的参数部分赋值给queryString
              将参数部分首先按照"&"拆分出每一组参数，再将每一组参数按照"="拆分为参数名与参数值
              并将参数名作为key，参数值作为value存入到parameters中。
                requestURI:/regUser
                queryString:username=&password=123456&nickname=chuanqi&age=22
                parameters:
                    key             value
                  username          ""
                  password          123456
                  nickname          chuanqi
                  age               22


           几种特殊情况
           1:表单中有空着不写的输入框
           例如:用户名不填
           /regUser?username=&password=123456&nickname=chuanqi&age=22

           2:表单中如果所有输入框没有指定name属性，此时表单提交则会忽略所有输入框
           /regUser?
         */
        String[] data = uri.split("\\?");
        requestURI = data[0];
        if(data.length>1){
            queryString = data[1];
            parseParameters(queryString);
        }
        System.out.println("requestURI:"+requestURI);
        System.out.println("queryString:"+queryString);
        System.out.println("parameters:"+parameters);
    }

    //解析参数
    private void parseParameters(String line){
        //line:username=%E8%8C%83%E4%BC%A0%E5%A5%87&password=123456&...
        try {
            //line:username=范传奇&password=123456&...
            line = URLDecoder.decode(line,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        //paraArr:[username=范传奇, password=123456, nickname=asan, age=22]
        String[] paraArr = line.split("&");
        //遍历每一组参数
        for (String para : paraArr){
            //para:username=
            String[] arr = para.split("=",2);
            //将参数名作为key,参数值作为value存入parameters
            parameters.put(arr[0], arr[1]);
        }
    }

    //解析消息头
    private void parseHeaders() throws IOException {
        while(true) {
            String line = readLine();
            if(line.isEmpty()){//如果消息头是一个空字符串说明读取到空行,表达消息头读取完毕了
                break;
            }
            System.out.println("消息头:" + line);
            //将消息头按照":空格"拆分出消息头的名字和值并存入到headers这个Map中
            //Host: localhost:8088
            String[] data = line.split(":\\s");
            //            host               , localhost:8088
            headers.put(data[0].toLowerCase(),data[1]);
        }
        System.out.println("headers:"+headers);
    }
    //解析消息正文
    private void parseContent(){
        System.out.println("开始解析消息正文========================"+method);
        //判断本次请求是否为POST请求
        if("post".equalsIgnoreCase(method)){
            //获取Content-Length的值来确定正文长度
            String length = getHeader("Content-Length");
            if(length!=null){
                int contentLength = Integer.parseInt(length);
                System.out.println("正文长度是:"+contentLength+"!!!!!!!!!!!!!!!");
                //根据Content-Length表达的长度创建等长的字节数组
                byte[] data = new byte[contentLength];
                try {
                    InputStream in = socket.getInputStream();
                    in.read(data);//将消息正文部分读取到数组中
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //根据Content-Type来对正文进行操作
                String contentType = getHeader("Content-Type");
                //判断是否为原GET请求提交时在URL中"?"右侧的数据
                if("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)){
                    String line = new String(data, StandardCharsets.ISO_8859_1);
                    System.out.println("正文数据:"+line);
                    parseParameters(line);
                }
//                //根据其他类型的正文来解析正文数据进行对应处理(比如含有附件的表单)
//                else if("xxxx/xxxx".equalsIgnoreCase(contentType)){
//
//                }


            }
        }


    }


    /**
     * 读取来自浏览器发送过来的一行字符串
     * @return
     * @throws IOException
     */
    private String readLine() throws IOException {
        InputStream in = socket.getInputStream();
        //记录读取的一行字符串内容
        StringBuilder builder = new StringBuilder();
        //pre记录上次读取的字符,cur记录本次读取的字符
        char pre='a',cur='a';
        int d;
        while((d = in.read())!=-1){
            cur = (char)d;//本次读取到的字符
            if(pre==13 && cur==10){//如果连续读取到了回车+换行则停止读取
                break;
            }
            builder.append(cur);
            pre = cur;//再下次读取字符前,将本次读取的字符纪录为"上次读取的字符"
        }
        return builder.toString().trim();
    }


    public String getMethod() {
        return method;
    }

    public String getUri() {
        return uri;
    }

    public String getProtocol() {
        return protocol;
    }

    public String getHeader(String name) {
        return headers.get(name.toLowerCase());
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getQueryString() {
        return queryString;
    }

    public String getParameter(String name) {
        return parameters.get(name);
    }
}







